<?php
	
	namespace Aspic\Cache;
	
	abstract class AbstractCache {
		
		use A\Preset\OptionTrait {
			__construct as optionTraitInit;
		}
		
		protected $_performanceMode;
		
		/**
		 *
		 * @var array An array like array('cacheKey' => cacheParsedData)
		 */
		protected $memCache;

		/**
		 * 
		 * @param type $cacheDir
		 * @param type $performanceMode When true, cache data for a unique key 
		 * will only be retrieve one time and save in memory for further demands.
		 */
		public function __construct($performanceMode = true) {
			$this->optionTraitInit();
			
			$this->memCache = array();
			
			$this->opt('performanceMode', $performanceMode);
		}
		
		/**
		 * Return an unique key for a view (to cache an HTML file that was generated from view data)
		 * @param View\View $view 
		 */
		public function getViewUniqueKey(View\View $view) {
			$fp = $view->getCurrentTemplatePath();
			$viewData = $view->getVars();
			
			$keyData = serialize(array($fp, $viewData));
			$key = md5($keyData);
			
			return $key;
		}

		/**
		 * Put in cache all the data that will be "print" to the browser
		 * @param type $key
		 * @param type $expiresData 
		 */
		public function cacheOutput($key, $expiresData = 86400) {
			$cache = $this;
			
			$callback = function() use ($cache, $key, $expiresData) {
				$buffer = ob_get_contents();
				ob_end_clean();
				
				$cache->cache($key, $buffer, $expiresData);
				print  $buffer;
			};
			
			register_shutdown_function($callback);
		}
		
		/**
		* Check if cache exists and is valid for $key
		* @param type $key 
		*/
		public function isCached($key, $performanceMode = null) {
			$data = $this->getCache($key, $performanceMode);
			
			if (is_null($data)) {
				return false;
			}
			else {
				if (is_int($data['expiresData'])) {
					$time = time();
					$expires = $time + $data['expiresData'];
					$isStillValid = ($data['expiresData'] <= 0 OR ($time < $expires));
				}
				elseif(!is_file($data['expiresData'])) {
					$filePath = $data['expiresData'];
					
					$fileTime = filemtime(realpath($filePath));
					$cacheFilePath = $this->getKeyFileName($key);
					$cacheFileTime = filemtime(realpath($this->opt('cacheDir').'/'.$cacheFilePath));

					$isStillValid = ($cacheFileTime >= $fileTime);
				}
				else {
					throw new A\Exception('Expires data "'.$data['expiresData'].'" is not valid');
				}
				
				return $isStillValid;
			}
		}
		
		/**
		 * Return data cached for $key
		 * @param type $key
		 * @return type 
		 */
		public function getCachedData($key) {
			$cache = $this->getCache($key);
			
			if (!is_null($cache)) {
				return $cache['data'];
			}
			else {
				return null;
			}
		}
		
		/**
		 * Return all information about a cache
		 */
		abstract protected function getCache($key, $performanceMode);
		
		/**
		 * Clean all cache data or only given key(s)
		 * @param array|string $keys Key(s) to clean
		 * @return type 
		 */
		abstract public function clean($keys = array());
		
		/**
		 * 
		 * @param type $key
		 * @param type $data
		 * @param integer|string $expiresData A delay in second by which cache will be unvalided or 
		 * a file path for which filemtime() functions will determine if cache is still valid (when given file 
		 * is more recent than cache file)
		 * @return type
		 */
		abstract public function cache($key, $data, $expiresData = 86400);
		
		/**
		 * Put the given cache contents in memory for further usage.
		 * To be used, this MUST BE CALLED in getCache method of child classes
		 * @param type $key
		 * @param type $data
		 * @param type $expiresData
		 */
	   protected function setMemCache($key, $data, $expiresData) {
			$this->memCache[$key] = array(
			   'data' => $data, 
			   'expiresData' => $expiresData, 
		   );
		}
	   
	   protected function getMemCache($key) {
		   return $this->memCache[$key];
	   }
	   
   }